package com.xiyou.weblog.common.exception;

import com.xiyou.weblog.common.enums.ResponseCodeEnum;
import com.xiyou.weblog.common.utils.Response;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Optional;

/**
 * @author xiyou
 * @description 全局异常处理
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 捕获自定义业务异常
     */
    @ExceptionHandler({ BizException.class })
    @ResponseBody
    public Response<Object> handleBizException(HttpServletRequest request, BizException e) {
        /* 下述代码中，通过 @ControllerAdvice 注解，
        我们将 GlobalExceptionHandler 声明为了全局异常处理类。
        在其中，定义了一个 handleBizException() 方法，
        并通过 @ExceptionHandler 注解指定只捕获 BizException 自定义业务异常。
        然后，打印了相关错误日志，并组合了统一的响应格式返回。*/
        log.warn("{} request fail, errorCode: {}, errorMsg: {}", request.getRequestURI(), e.getErrorCode(),e.getErrorMsg());
        return Response.fail(e);
    }

    /**
     * 其他异常
     */
    @ExceptionHandler({ Exception.class })
    @ResponseBody
    public Response<Object> handleOtherException(HttpServletRequest request, Exception e) {
        log.error("{} request error，" , request.getRequestURI(),e);
        return Response.fail(ResponseCodeEnum.SYSTEM_ERROR);
    }

    /**
     * 捕获参数校验异常
     */
    @ExceptionHandler({ MethodArgumentNotValidException.class })
    @ResponseBody
    public Response<Object> handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
        // 参数错译异常码
        String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode();

        // 捕获BindingResult
        BindingResult bindingResult = e.getBindingResult();

        StringBuilder stringBuilder = new StringBuilder();
        // 获取校验不通过的字段，并组合错误信息，格式为： email 邮箱格式不正确, 当前值: '123124qq.com';
        Optional.of(bindingResult.getFieldErrors()).ifPresent(errors ->{
            errors.forEach(error -> {
                stringBuilder.append(error.getField())
                        .append(" ")
                        .append(error.getDefaultMessage())
                        .append("，当前值：'")
                        .append(error.getRejectedValue())
                        .append("';");
            });
        });

        // 错误信息
        String errorMsg = stringBuilder.toString();

        log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMsg);

        return Response.fail(errorCode, errorMsg);
    }


    @ExceptionHandler({ AccessDeniedException.class })
    public void throwAccessDeniedException(AccessDeniedException e) throws AccessDeniedException {
        // 捕获到鉴权失败异常，主动抛出，交给 RestAccessDeniedHandler 去处理
        log.info("============= 捕获到 AccessDeniedException");
        throw e;
    }
}
